package com.hznu.stmanage.utils;

import com.hznu.stmanage.beans.EntitySupport;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.builder.xml.dynamic.ForEachSqlNode;
import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.executor.parameter.DefaultParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.property.PropertyTokenizer;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandler;
import org.apache.ibatis.type.TypeHandlerRegistry;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Properties;

/**
 * <p>Title: PaginationInterceptor.java</p>
 * <p>Description: 物理分页，拦截器</p>
 * <p>Copyright: </p>
 * <p>Company: </p>
 * <p>createTime:2012-6-11 下午6:39:39</p>
 * @author xwf
 * @version 1.0
 */
//@Intercepts({@Signature(type=Executor.class,method="query",args={ MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class })})  
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) })
public class PaginationInterceptor implements Interceptor {
	private EntitySupport entity;

	private static final Log log = LogFactory.getLog(PaginationInterceptor.class);
	/*	public Object intercept(Invocation invocation) throws Throwable {
		StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
		MetaObject metaStatementHandler = MetaObject.forObject(statementHandler);

//		RowBounds rowBounds = (RowBounds) metaStatementHandler.getValue("delegate.rowBounds");
//		if (rowBounds == null || rowBounds == RowBounds.DEFAULT) {
//			return invocation.proceed();
//		}

		DefaultParameterHandler defaultParameterHandler = (DefaultParameterHandler) metaStatementHandler.getValue("delegate.parameterHandler");
//		Map parameterMap = (Map) defaultParameterHandler.getParameterObject();
		Object parameter = defaultParameterHandler.getParameterObject();    
//		Object sidx = parameterMap.get("_sidx");
//		Object sord = parameterMap.get("_sord");

		String originalSql = (String) metaStatementHandler
				.getValue("delegate.boundSql.sql");

//		if (sidx != null && sord != null) {
//			originalSql = originalSql + " order by " + sidx + " " + sord;
//		}
		
		if(!hasPaging(parameter)){    
            return invocation.proceed();    
        }  
        
        int offset=0; int limit=0;
        if(parameter instanceof EntitySupport){
        	EntitySupport entity = (EntitySupport) parameter;
        	Object sidx = entity.getSortField();    
        	Object sord = entity.getSortOrder(); 
        	offset = entity.getPageIndex()*entity.getPageSize();    
			limit = offset+entity.getPageSize(); 
			
	        if(sidx != null && sord != null){    
	            originalSql = originalSql + " order by " + sidx + " " + sord;    
	        } 
        	
        }

		Configuration configuration = (Configuration) metaStatementHandler
				.getValue("delegate.configuration");

		Dialect.Type databaseType = null;
		try {
			databaseType = Dialect.Type.valueOf(configuration.getVariables()
					.getProperty("dialect").toUpperCase());
		} catch (Exception e) {
			// ignore
		}
		if (databaseType == null) {
			databaseType = Dialect.Type.valueOf("oracle".toUpperCase());
			log.debug("没有配置的数据库类型，采用默认形式oracle");
		}else{
			log.debug("配置的数据库类型为 : " + databaseType);
		}
		Dialect dialect = null;
		String sql = "";
		switch (databaseType) {
			case ORACLE:
				dialect = new DBDialect();
				sql = dialect.getOracleString(originalSql, offset,limit);
				break;
			case MYSQL:
				dialect = new DBDialect();
				sql = dialect.getMysqlString(originalSql, offset,limit);
				break;
			default:
				dialect = new DBDialect();
				sql = dialect.getOracleString(originalSql, offset,limit);
				break;
		}
		if (log.isDebugEnabled()) {
			BoundSql boundSql = statementHandler.getBoundSql();
			log.debug("生成分页SQL : " + boundSql.getSql());
		}
		metaStatementHandler.setValue("delegate.boundSql.sql", sql);
		metaStatementHandler.setValue("delegate.rowBounds.offset",
				RowBounds.NO_ROW_OFFSET);
		metaStatementHandler.setValue("delegate.rowBounds.limit",
				RowBounds.NO_ROW_LIMIT);
		
		return invocation.proceed();
	}*/
	/**
	 * 拦截器执行的内容
	 * @date 2012-6-21 上午9:52:01
	 * @param invocation  动态代理的3个参数
	 * @return
	 * @author cxp
	 * @throws Throwable
	 */
	public Object intercept(Invocation invocation) throws Throwable {
		StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
		MetaObject metaStatementHandler = MetaObject.forObject(statementHandler);

//		RowBounds rowBounds = (RowBounds) metaStatementHandler.getValue("delegate.rowBounds");
//		if (rowBounds == null || rowBounds == RowBounds.DEFAULT) {
//			return invocation.proceed();
//		}

		DefaultParameterHandler defaultParameterHandler = (DefaultParameterHandler) metaStatementHandler.getValue("delegate.parameterHandler");
//		Map parameterMap = (Map) defaultParameterHandler.getParameterObject();
		Object parameter = defaultParameterHandler.getParameterObject();//得到的是mybatis传入的对象
//		Object sidx = parameterMap.get("_sidx");
//		Object sord = parameterMap.get("_sord");

		String originalSql = (String) metaStatementHandler
				.getValue("delegate.boundSql.sql");
		String originalSql2 = originalSql;//用于查询count的sql
//		if (sidx != null && sord != null) {
//			originalSql = originalSql + " order by " + sidx + " " + sord;
//		}

		if(!hasPaging(parameter)){
			return invocation.proceed();//直接执行，跳过下面的内容
		}

		int offset=0; int limit=0;
		int total = 0 ;
		EntitySupport entity = null;
		if(parameter instanceof EntitySupport){
			entity = (EntitySupport) parameter;
			Object sidx = entity.getSortField();
			Object sord = entity.getSortOrder();
			offset = entity.getPageIndex()*entity.getPageSize();
			limit = entity.getPageSize();
			total = entity.getTotal();
			/* oracle
			if(sidx!=null&&!"".equals(sidx)){
				//已用非row_number函数排序过的
				if(originalSql.toUpperCase().indexOf("ORDER")>=0 && originalSql.toUpperCase().indexOf("ROW_NUMBER") == 0){
					originalSql = originalSql + "," + sidx;
				}else{
					originalSql = originalSql + " order by " + sidx;
				}

				if(sord!=null&&!"".equals(sord)){
					originalSql = originalSql + " " + sord;
				}
			}*/

			if(sidx!=null&&!"".equals(sidx)){
				//已用非row_number函数排序过的
				if(originalSql.toUpperCase().indexOf("ORDER")>=0){
					originalSql = "select * from (" + originalSql + ") tb_alias order by " + sidx;
				}else{
					originalSql = originalSql + " order by " + sidx;
				}

				if(sord!=null&&!"".equals(sord)){
					originalSql = originalSql + " " + sord;
				}
			}

		}

		Configuration configuration = (Configuration) metaStatementHandler
				.getValue("delegate.configuration");

		BoundSql boundSql = statementHandler.getBoundSql();
		// 得到总记录数
		if (total == 0) {
			StringBuffer countSql = new StringBuffer(originalSql.length() + 100);
			countSql.append("select count(1) from (").append(originalSql2)
					.append(") t");
			Connection connection = (Connection)invocation.getArgs()[0];
			PreparedStatement countStmt = connection.prepareStatement(countSql
					.toString());
			//BoundSql countBS = new BoundSql(configuration,countSql.toString(), boundSql.getParameterMappings(),boundSql.getParameterObject());
			setParameters(countStmt, configuration, boundSql, boundSql.getParameterObject());
			ResultSet rs = countStmt.executeQuery();
			if (rs.next()) {
				total = rs.getInt(1);
			}
			rs.close();
			countStmt.close();
//			connection.close();
		}
		if(entity!=null){
			entity.setTotal(total);
		}

		Dialect.Type databaseType = null;
		try {
			databaseType = Dialect.Type.valueOf(configuration.getVariables()
					.getProperty("dialect").toUpperCase());
		} catch (Exception e) {
			// ignore
		}
		if (databaseType == null) {
			databaseType = Dialect.Type.valueOf("oracle".toUpperCase());
			log.debug("没有配置的数据库类型，采用默认形式oracle");
		}else{
			log.debug("配置的数据库类型为 : " + databaseType);
		}
		Dialect dialect = null;
		String sql = "";
		switch (databaseType) {
			case ORACLE://oracle的处理方式
				dialect = new DBDialect();
				sql = dialect.getOracleString(originalSql, offset,limit);
				break;
			case MYSQL://mysal的处理方式
				dialect = new DBDialect();
				sql = dialect.getMysqlString(originalSql, offset,limit);
				break;
			default://默认的
				dialect = new DBDialect();
				sql = dialect.getOracleString(originalSql, offset,limit);
				break;
		}
		if (log.isDebugEnabled()) {
			log.debug("生成分页SQL : " + boundSql.getSql());
		}
		metaStatementHandler.setValue("delegate.boundSql.sql", sql);
		metaStatementHandler.setValue("delegate.rowBounds.offset",
				RowBounds.NO_ROW_OFFSET);
		metaStatementHandler.setValue("delegate.rowBounds.limit",
				RowBounds.NO_ROW_LIMIT);

		return invocation.proceed();
	}

	/**
	 * 对SQL参数(?)设值,参考org.apache.ibatis.executor.parameter.DefaultParameterHandler
	 * @param ps
	 * @param configuration
	 * @param boundSql
	 * @param parameterObject
	 * @throws java.sql.SQLException
	 */
	private void setParameters(PreparedStatement ps, Configuration configuration, BoundSql boundSql, Object parameterObject) throws SQLException {
//        ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());    
		List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
		if (parameterMappings != null) {
//            Configuration configuration = mappedStatement.getConfiguration();    
			TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
			MetaObject metaObject = parameterObject == null ? null: configuration.newMetaObject(parameterObject);
			for (int i = 0; i < parameterMappings.size(); i++) {
				ParameterMapping parameterMapping = parameterMappings.get(i);
				if (parameterMapping.getMode() != ParameterMode.OUT) {
					Object value;
					String propertyName = parameterMapping.getProperty();
					PropertyTokenizer prop = new PropertyTokenizer(propertyName);
					if (parameterObject == null) {
						value = null;
					} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
						value = parameterObject;
					} else if (boundSql.hasAdditionalParameter(propertyName)) {
						value = boundSql.getAdditionalParameter(propertyName);
					} else if (propertyName.startsWith(ForEachSqlNode.ITEM_PREFIX)&& boundSql.hasAdditionalParameter(prop.getName())) {
						value = boundSql.getAdditionalParameter(prop.getName());
						if (value != null) {
							value = configuration.newMetaObject(value).getValue(propertyName.substring(prop.getName().length()));
						}
					} else {
						value = metaObject == null ? null : metaObject.getValue(propertyName);
					}
					TypeHandler typeHandler = parameterMapping.getTypeHandler();
					if (typeHandler == null) {
						throw new ExecutorException("There was no TypeHandler found for parameter "+ propertyName + " of statement ");
					}
					typeHandler.setParameter(ps, i + 1, value, parameterMapping.getJdbcType());
				}
			}
		}
	}


	/*
	 * (non-Javadoc)
	 * 
	 * @see org.apache.ibatis.plugin.Interceptor#plugin(java.lang.Object)
	 */
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}
	/**
	 * 判断是否分页
	 * @param parameter
	 * @return
	 */

	private boolean hasPaging(Object parameter){
		if(parameter instanceof EntitySupport){
			EntitySupport entity = (EntitySupport) parameter;

			if(entity.isNoPaging()){
				return false;
			}

			int pageindex = entity.getPageIndex();
			int pagesize = entity.getPageSize();
			if(pageindex>-1 && pagesize>0 && (pageindex+1)*pagesize<RowBounds.NO_ROW_LIMIT){
				return true;
			}
		}
		return false;
	}
	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.apache.ibatis.plugin.Interceptor#setProperties(java.util.Properties)
	 */
	public void setProperties(Properties arg0) {
		// TODO Auto-generated method stub

	}

}